/**
 * 
 */
package org.xteam.sled.semantic;

import java.util.ArrayList;
import java.util.List;

import org.xteam.parser.runtime.Span;
import org.xteam.sled.ast.DefaultSledVisitor;
import org.xteam.sled.ast.EqOp;
import org.xteam.sled.ast.GeOp;
import org.xteam.sled.ast.GtOp;
import org.xteam.sled.ast.LeOp;
import org.xteam.sled.ast.LtOp;
import org.xteam.sled.ast.NeOp;
import org.xteam.sled.ast.Relop;
import org.xteam.sled.model.IField;
import org.xteam.sled.model.RangeConstraint;


public class RangeConstraintsMaker<T extends IField> extends DefaultSledVisitor {
	
	private int value;
	private T field;
	private List<RangeConstraint<T>> constraints = new ArrayList<RangeConstraint<T>>();
	private IErrorContext context;
	private Span span;
	
	public RangeConstraintsMaker(IErrorContext context, Span span, T field, int value) {
		this.context = context;
		this.field = field;
		this.value = value;
		this.span = span;
	}
	
	public RangeConstraint<T>[] make(Relop relop) {
		relop.visit(this);
		return constraints.toArray(new RangeConstraint[constraints.size()]);
	}

	public void visitEqOp(EqOp op) {
		constraints.add(new RangeConstraint<T>(field, new Range(value)));
	}
	
	public void visitLtOp(LtOp op) {
		if (value < 0)
			context.reportError(span, "Impossible constraint " + field.name() + " < " + value);
		else if (value >= field.fieldmax())
			context.reportError(span, "Constraint value too large in " + field.name() + " < " + value);
		else
			constraints.add(new RangeConstraint<T>(field, new Range(0, value)));
	}
	
	public void visitLeOp(LeOp op) {
		if (value < 0)
			context.reportError(span, "Impossible constraint " + field.name() + " <= " + value);
		if (value >= field.fieldmax())
			context.reportError(span, "Constraint value too large in " + field.name() + " <= " + value);
		constraints.add(new RangeConstraint<T>(field, new Range(0, value+1)));
	}
	
	public void visitGtOp(GtOp op) {
		if (value+1 >= field.fieldmax())
			context.reportError(span, "Impossible constraint " + field.name() + " > " + value);
		if (value < ~1)
			context.reportError(span, "Constraint value too small in " + field.name() + " > " + value);
		constraints.add(new RangeConstraint<T>(field, new Range(value+1, field.fieldmax())));
	}
	
	public void visitGeOp(GeOp op) {
		if (value >= field.fieldmax())
			context.reportError(span, "Impossible constraint " + field.name() + " >= " + value);
		if (value < 0)
			context.reportError(span, "Constraint value too small in " + field.name() + " >= " + value);
		constraints.add(new RangeConstraint<T>(field, new Range(value, field.fieldmax())));
	}
	
	public void visitNeOp(NeOp op) {
		if (value != 0)
			constraints.add(new RangeConstraint<T>(field, new Range(0, value)));
		if (value+1 != field.fieldmax())
			constraints.add(new RangeConstraint<T>(field, new Range(value+1, field.fieldmax())));
	}
}